Factory Pattern
2 min readFactory Pattern
TL;DR
Centralize creation of objects (like API handlers or executors) based on configuration or environment.
How it works
🧩 Example — Trading platform executor factory
public interface ITradeExecutor
{
void Execute(Order order);
}
public class Mt4Executor : ITradeExecutor
{
public void Execute(Order order) =>
Console.WriteLine($"[MT4] Executing {order.Symbol}");
}
public class Mt5Executor : ITradeExecutor
{
public void Execute(Order order) =>
Console.WriteLine($"[MT5] Executing {order.Symbol}");
}
public static class ExecutorFactory
{
public static ITradeExecutor Create(string platform) => platform switch
{
"MT4" => new Mt4Executor(),
"MT5" => new Mt5Executor(),
_ => throw new ArgumentException("Unknown platform")
};
}
// --- Usage ---
var platform = "MT5";
var executor = ExecutorFactory.Create(platform);
executor.Execute(new Order { Symbol = "USDJPY", Amount = 5000 });
✅ Why it matters:
- Simplifies platform switching (MT4, MT5, FIX, cTrader).
- New platforms require no refactor — just a new class and switch entry.
Quick recall Q&A
When object creation depends on runtime context (config, tenant, instrument) and you want to isolate creation logic from consumers. Factories prevent scattered new calls and keep construction consistent.
You can register implementations in DI and inject Func<Key, ITradeExecutor> or an IExecutorFactory that resolves services by key. This keeps factories composable with scoped dependencies.
Use a dictionary of delegates, reflection-based registration, or DI IServiceProvider lookups keyed by platform. Alternatively, combine with the Strategy pattern so each executor registers itself.
When you need to create families of related objects (e.g., executor + validator + serializer per platform), an abstract factory ensures compatible components are produced together.
Tests can inject fake factories returning mock executors, isolating code under test without hitting real integrations. It also simplifies verifying that the correct executor is chosen for a scenario.
Load mappings from configuration or feature flags, then let the factory instantiate types via DI. This enables runtime toggles (e.g., route VIP tenants to a premium executor) without code changes.
When only two concrete types exist and creation logic is trivial. Start simple, and introduce a factory once switching logic repeats or needs shared validation/logging.
Keep them focused on creation. Any orchestration, validation, or logging should be delegated to other components or decorators so factories don't become god objects.
Yes—define methods returning Task<T> if creation involves I/O (e.g., pulling secrets). Just ensure callers understand the lifecycle and avoid blocking .Result.
Register each concrete type and a factory delegate: services.AddTransient<ExecutorFactory>(); or services.AddSingleton<Func<string, ITradeExecutor>>(provider => key => provider.GetRequiredKeyedService<ITradeExecutor>(key));.